home *** CD-ROM | disk | FTP | other *** search
- /* gif2rpc.c
- * AUTHOR: Cy Booker, cy@cheepnis.demon.co.uk
- * LICENSE: FreeWare, Copyright (c) 1995 Cy Booker, but see below
- * PURPOSE: convert GIF file to RISC OS sprite
- *
- * the lzw decoding part contains code that came with the following header:
- *
- *================================================================================================
- * DECODE.C - An LZW decoder for GIF
- * Copyright (C) 1987, by Steven A. Bennett
- *
- * Permission is given by the author to freely redistribute and include
- * this code in any program as long as this credit is given where due.
- *
- * In accordance with the above, I want to credit Steve Wilhite who wrote
- * the code which this is heavily inspired by...
- *
- * GIF and 'Graphics Interchange Format' are trademarks (tm) of
- * Compuserve, Incorporated, an H&R Block Company.
- *
- * Release Notes: This file contains a decoder routine for GIF images
- * which is similar, structurally, to the original routine by Steve Wilhite.
- * It is, however, somewhat noticably faster in most cases.
- *================================================================================================
- *
- * the GIF decoding is based on the specification:
- *
- *================================================================================================
- * GRAPHICS INTERCHANGE FORMAT(sm)
- * Version 89a
- * (c)1987,1988,1989,1990
- * Copyright
- * CompuServe Incorporated
- * Columbus, Ohio
- *================================================================================================
- *
- * note that there is no patent infringement involved in DECODING gif files...
- *
- * everything else you can blame me (cjb) for
- *
- * oh yeah, usual disclaimers apply...
- *
- * CJB: 1995.10.01: made processing code a library
- *
- * TODO: so 16bit output is *quickly* downgraded to 8/4/2/1 if ok to do so
- */
-
- #include <assert.h>
- #include <ctype.h>
- #include <locale.h> /* so can force lower case */
- #include <setjmp.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
-
-
- #include "OS:colourtrans.h"
- #include "OS:hourglass.h"
- #include "OS:macros.h"
- #include "OS:osfile.h"
- #include "OS:osspriteop.h"
-
-
- #include "gif2rpc:map8bpp.h"
- #include "gif2rpc:map16bpp.h"
- #include "gif2rpc:process_gif.h"
-
- #include "16bpp_48bit.h.16bpp_48bit"
- #include "16bpp_66bit.h.16bpp_66bit"
- #include "8bpp.h.8bpp"
-
-
-
- /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- */
-
- #ifndef DEBUG_LZW
- #define DEBUG_LZW 0
- #endif /* DEBUG_LZW */
-
- #define LZW_MAX_CODES (4095)
-
- #define FAIL_NOT_GIF (10)
- #define FAIL_CORRUPT_LZW (11)
- #define FAIL_SHORT_FILE (12)
- #define FAIL_BAD_RECORD (13)
- #define FAIL_GRAPHIC_EXTENSION_SIZE (14)
- #define FAIL_NO_IMAGE (15)
- #define FAIL_IMAGE_DIMENSIONS (16)
- #define FAIL_GRAPHIC_EXTENSION_TERMINATOR (17)
- #define FAIL_INTERNAL_PROCESS_MASK (18)
- #define FAIL_INTERNAL_PROCESS_IMAGE (29)
- #define FAIL_NO_PALETTE (21)
- #define FAIL_INCOMPLETE_LAST_ROW (22)
- #define FAIL_EXTRA_DATA_AFTER_IMAGE (23)
- #define FAIL_BAD_EOF_IMAGE (24)
-
- #define GIF2RPC_PARAMS "gif2rpc$Options" /* system variable */
- #define SPRITE_NAME "gif2rpc" /* default sprite name */
-
- #define VBUF (1024) /* byte size of log file buffer */
-
- #define YESNO(B) ((B) ? "yes" : "no")
-
- #define MODE(LOG2BPP, XRES, YRES) ((os_mode)(\
- (((LOG2BPP) + 1) << osspriteop_TYPE_SHIFT)\
- | ((XRES) << osspriteop_YRES_SHIFT)\
- | ((YRES) << osspriteop_XRES_SHIFT)\
- | 1))
-
- #define LSR(X, S) (((unsigned int)(X)) >> (S)) /* portable */
-
- #define WORD_WIDTH(P) LSR((P) + 31, 5) /* P is BIT width */
-
-
-
- /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- */
-
- typedef struct {
- byte *bitmap;
- int line_length;
- } bitmapinfo;
-
- typedef struct {
- bitmapinfo source;
- bitmapinfo dest;
- int bpp;
- os_coord pixel_size;
- } reducebitmap;
-
-
- typedef bool (*process_gif_fn)(const process_gif *);
-
-
- /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- */
-
- static void default_wimppalette(
- os_colour *palette,
- int lb_bpp);
- static byte fail(void);
- static process_gif_fn filter_fn(
- int index);
- static byte get_byte(void);
- static byte get_image_byte(void);
- static byte get_image_byte_aux(void);
- static int get_word(void);
- static void gif_get_image_desc(void);
- static void gif_lzw_decode(void);
- static void gif_parse(void);
- static void gif_parse_extension(
- int extension);
- static byte *gif_row(void);
- static void gif_skip_data(void);
- static void insert_bytes_before_image(
- osspriteop_area *area,
- osspriteop_header *sprite,
- int siz);
- static void load_file(void);
- static void log_filename(
- const char *name);
- static int lzw_get_next_code(void);
- static void lzw_init(
- int size);
- static void prepare_sprite(void);
- static void process_cli(
- char **argv);
- static bool process_gif_32bpp(
- const process_gif *p);
- static void process_mask(void);
- static void process_image(void);
- static void reduce_bitmap(
- const reducebitmap *rb);
- static void reduce_sprite_lb_bpp(
- int lb_bpp);
- static int stricmp(
- const char *sleft,
- const char *sright);
- static void turn_off_hourglass(void);
-
-
-
- /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- */
-
- static struct {
- osspriteop_area *area;
- osspriteop_header *sprite;
- int lb_bpp;
- os_coord pixel_size;
- os_coord dpi;
- bool transparent_p;
- bitmapinfo image;
- bitmapinfo mask;
- int filter; /* index into Gfilters array */
- } Gsprite;
-
- static struct GIF {
- byte *file_buffer;
- unsigned int file_siz;
- unsigned int file_pos;
- struct {
- os_coord size; /* size of screen, in pixels */
- int bpp; /* bpp of screen */
- int background; /* background palette index (ignored) */
- int aspect_ratio; /* weird format */
- bool palette_p; /* was a global palette defined? */
- bool palette_sorted_p; /* is palette pre-sorted? (ignored) */
- int palette_bpp; /* how many bpp available in palette */
- os_colour palette[256];
- } screen;
- struct {
- os_coord topleft;
- os_coord size; /* technically, should be inside screen */
- bool interlace_p;
- bool palette_p;
- bool palette_sorted_p;
- int palette_bpp;
- os_colour palette[256]; /* this is the palette for the image */
- } image;
- struct {
- int dispose; /* ignore */
- bool user_input_p; /* ignore */
- bool transparent_p; /* is the image transparent */
- int delay_time; /* ignore */
- int transparent; /* this is the transparent colour index */
- } control;
- bool image_p;
- bool control_p; /* control section found? */
- int input_bpp; /* ie for image/screen {1,2,3,4,5,6,7,8} */
- int output_lb_bpp; /* requested lb_bpp {0,1,2,3,4,5} */
- jmp_buf abortion;
- } Ggif;
-
- static struct {
- int current_size;
- int clear;
- int ending;
- int newcodes;
- int top_slot;
- int slot;
- int nbits_left;
- int bad_code_count;
- byte b1; /* one byte bit buffer */
- byte stack[LZW_MAX_CODES + 1]; /* Stack for storing pixels */
- byte suffix[LZW_MAX_CODES + 1]; /* Suffix table */
- unsigned int prefix[LZW_MAX_CODES + 1]; /* Prefix linked list */
- int row; /* offset in current interlace bit */
- int interlace_part; /* 0..3 */
- int block_count; /* bytes left in current gif file block */
- int real_row; /* used by hourglass off */
- } Glzw;
-
- static struct {
- char *gif;
- char *sprite;
- char *filter;
- char *log;
- char *name;
- bool help_p;
- bool square_p;
- bool fussy_p;
- bool quiet_p;
- bool hourglass_p;
- bool stats_p;
- bool accurate_p;
- bool force_66_p;
- bool autoname_p;
- int bpp;
- int verbose;
- } Gargs;
-
- static process_gif Gpg;
-
- static FILE *Glog = stdout;
-
- static struct {
- const char *name;
- process_gif_fn fn_8bpp;
- process_gif_fn fn_16bpp_48bit;
- process_gif_fn fn_16bpp_66bit;
- } Gfilters[] = {
- #define FILTER(X) {#X, process_gif_8bpp_ ##X, process_gif_16bpp_ ##X## _48bit, process_gif_16bpp_ ##X## _66bit}
- /* first */ FILTER(nearest),
- /* second */ FILTER(dither2x2),
- FILTER(sierra2_4a),
- FILTER(floyd_steinberg),
- FILTER(sierra3),
- FILTER(sierra2),
- FILTER(burkes),
- FILTER(stucki),
- FILTER(jarvis_judice_ninke)};
-
-
- static struct {
- os_t started;
- os_t decoded;
- os_t processed;
- } Gwhen;
-
-
-
- static os_colour Goutput_palette[256];
-
-
- /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- */
-
- int main(
- int argc,
- char **argv) {
- int code;
-
- NOT_USED(argc);
- setlocale(LC_ALL, ""); /* current locale, and not C locale */
-
- process_cli(argv);
-
- if (Gargs.stats_p) {
- Gwhen.started = os_read_monotonic_time();
- }
- if (Gargs.log) {
- if (!freopen(Gargs.log, "a", stderr)) {
- fprintf(stderr, "Failed to redirect output\n");
- return EXIT_FAILURE;
- }
- setvbuf(stderr, NULL, _IONBF, 0);
- Glog = stderr;
- }
-
- load_file();
-
- code = setjmp(Ggif.abortion);
- if (code) {
- if (!Gargs.quiet_p) {
- fprintf(stderr, "gif2rpc: %s: error %d @ %#x\n", Gargs.gif, code, Ggif.file_pos);
- }
- return EXIT_FAILURE;
- }
-
- gif_parse();
-
- return EXIT_SUCCESS;
- }
-
-
-
- /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- */
-
- static void add_arg(
- char **pcli,
- const char *arg) {
- char *cli;
-
- assert(pcli);
- assert(arg);
-
- cli = (*pcli)
- ? realloc(*pcli, strlen(*pcli) + 1 + strlen(arg) + 1)
- : malloc(strlen(arg) + 1);
- if (!cli) {
- exit(EXIT_FAILURE);
- }
- if (*pcli) {
- strcat(cli, " ");
- strcat(cli, arg);
- } else {
- strcpy(cli, arg);
- }
- *pcli = cli;
- }
-
-
-
- /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- */
-
- static void gif_parse(void) {
- bits flags;
- int i, size;
- os_colour colour;
- byte record;
- byte dummy;
- int code;
- int bpp, siz, in_file_siz, out_file_siz, spr_file_siz;
- bits duration_decode, duration_process, duration_all;
- os_error *error;
-
- if ((get_byte() != 'G')
- || (get_byte() != 'I')
- || (get_byte() != 'F')) {
- longjmp(Ggif.abortion, FAIL_NOT_GIF);
- }
- dummy = get_byte();
- dummy = get_byte();
- dummy = get_byte(); /* skip version string */
- /*
- * read screen description
- */
- Ggif.screen.size.x = get_word();
- Ggif.screen.size.y = get_word();
- flags = get_byte();
- Ggif.screen.bpp = LSR(flags & 0x70, 4) + 1;
- Ggif.screen.palette_bpp = (flags & 0x07) + 1;
- Ggif.screen.palette_p = ((flags & 0x80) != NONE);
- Ggif.screen.palette_sorted_p = ((flags & 0x08) != NONE);
- Ggif.screen.background = get_byte();
- Ggif.screen.aspect_ratio = get_byte();
- if (Ggif.screen.palette_p) {
- /*
- * global colour map
- */
- size = 1 << Ggif.screen.palette_bpp;
- for (i= 0; (i < size); i++) {
- colour = get_byte() << 8; /* should we do get_byte() * 255 / ((1 << palette_bpp) - 1) ? */
- colour |= get_byte() << 16;
- colour |= get_byte() << 24;
- Ggif.screen.palette[i] = colour;
- }
- }
- if (Gargs.verbose > 0) {
- fprintf(Glog, " screen size= (%d, %d), screen bpp= %d, palette_bpp= %d, background= %d, palette= %s, sorted= %s, aspect_ratio= %d\n",
- Ggif.screen.size.x, Ggif.screen.size.y,
- Ggif.screen.bpp,
- Ggif.screen.palette_bpp,
- Ggif.screen.background,
- YESNO(Ggif.screen.palette_p),
- YESNO(Ggif.screen.palette_sorted_p),
- Ggif.screen.aspect_ratio);
- }
- for (;;) {
- record = get_byte();
- switch (record) {
- case 0x2c:
- if (Ggif.image_p) {
- /* ignore multiple images */
- return;
- }
- gif_get_image_desc();
- if ((Ggif.image.palette_p == NONE) && (Ggif.screen.palette_p == NONE)) {
- longjmp(Ggif.abortion, FAIL_NO_PALETTE);
- }
- Ggif.image_p = TRUE;
- Ggif.input_bpp = (Ggif.image.palette_p) ? Ggif.image.palette_bpp : Ggif.screen.palette_bpp;
- prepare_sprite();
- if (Gargs.hourglass_p) {
- atexit(turn_off_hourglass);
- xhourglass_on();
- }
- if (!Gargs.fussy_p) {
- /*
- * set up a dummy jump buffer to catch any and all errors in the decoding
- */
- code = setjmp(Ggif.abortion);
- if (code == 0) {
- gif_lzw_decode();
- } else {
- if (!Gargs.quiet_p) {
- fprintf(stderr, "gif2rpc: %s: (ignored) error %d @ %#x\n", Gargs.gif, code, Ggif.file_pos);
- }
- }
- /*
- * repeat code in main to handle any processing bugs!
- */
- code = setjmp(Ggif.abortion);
- if (code) {
- if (!Gargs.quiet_p) {
- fprintf(stderr, "gif2rpc: %s: error %d @ %#x\n", Gargs.gif, code, Ggif.file_pos);
- }
- exit(EXIT_FAILURE);
- }
- } else {
- gif_lzw_decode();
- }
- if (Gargs.stats_p) {
- Gwhen.decoded = os_read_monotonic_time();
- }
-
- if (Gargs.hourglass_p) {
- xhourglass_percentage(0);
- }
-
- if (Gsprite.transparent_p) {
- /*
- * must generate a mask from raw 8-bit data before processing `image'
- *
- */
- process_mask();
- }
- process_image();
-
- if (Gargs.verbose > 0) {
- fprintf(Glog, " saving `%s', size %#x\n", Gargs.sprite, Gsprite.area->used);
- }
- if (Gargs.hourglass_p) {
- xhourglass_percentage(0);
- }
-
- if (Gargs.stats_p) {
- Gwhen.processed = os_read_monotonic_time();
- }
-
- /*
- * save sprite file
- */
- error = xosspriteop_save_sprite_file(osspriteop_USER_AREA, Gsprite.area, Gargs.sprite);
- if (error) {
- if (!Gargs.quiet_p) {
- fprintf(stderr, "gif2rpc: error %#x `%s' saving file `%s'\n",
- error->errnum, error->errmess, Gargs.sprite);
- }
- }
-
- if (Gargs.stats_p) {
- duration_decode = Gwhen.decoded - Gwhen.started;
- duration_process = Gwhen.processed - Gwhen.decoded;
- duration_all = os_read_monotonic_time() - Gwhen.started;
- fprintf(Glog, "gif2rpc ");
- log_filename(Gargs.gif);
- fprintf(Glog, "(%4dx%4d:%d%c%c.%c%c%c.%c) -> ",
- Gsprite.pixel_size.x,
- Gsprite.pixel_size.y,
- Ggif.input_bpp,
- (Ggif.screen.palette_p) ? 'p' : ' ',
- (Ggif.screen.palette_sorted_p) ? 's' : ' ',
- (Ggif.image.interlace_p) ? 'i' : ' ',
- (Ggif.image.palette_p) ? 'p' : ' ',
- (Ggif.image.palette_sorted_p) ? 's' : ' ',
- ((Ggif.control_p) && (Ggif.control.transparent_p)) ? 't' : ' ');
- log_filename(Gargs.sprite);
- in_file_siz = Ggif.file_siz;
- out_file_siz = Gsprite.area->used - 4;
- bpp = 1 << ("01223333"[Ggif.input_bpp-1] - '0');
- siz = Gsprite.pixel_size.y * 4 * WORD_WIDTH(Gsprite.pixel_size.x * bpp);
- spr_file_siz = (sizeof(osspriteop_area) - 4)
- + sizeof(osspriteop_header)
- + (256 * sizeof(os_colour_pair))
- + siz
- + ((Gsprite.transparent_p) ? siz : 0);
-
- fprintf(Glog, "(%.3s) %8d %6.2f %6.2f%% %s\n",
- " 2 4 1625632K16M"+(3*Gsprite.lb_bpp),
- spr_file_siz - out_file_siz,
- duration_all / 100.0,
- (duration_process * 100.0) / (double)duration_all,
- Gfilters[Gsprite.filter].name);
- }
- if (!Gargs.fussy_p) {
- /*
- * don't bother checking for multiple images, or validating an end of file
- */
- return;
- }
- break;
-
- case 0x21:
- gif_parse_extension(get_byte());
- break;
-
- case 0x3b: /* marks end of file */
- if (!Ggif.image_p) {
- longjmp(Ggif.abortion, FAIL_NO_IMAGE);
- }
- return;
-
- default:
- if (!Gargs.quiet_p) {
- fprintf(stderr, "gif2rpc: %s: Bad record %#x\n", Gargs.gif, record);
- }
- longjmp(Ggif.abortion, FAIL_BAD_RECORD);
- }/* switch (record) */
- }/* for (;;) */
- }
-
-
-
- /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- */
-
- static void gif_parse_extension(
- int extension) {
- int size, i;
- byte buffer[256];
-
- switch (extension) {
- case 0x00: /* change sierra2_4a behaviour! */
- break;
-
- case 0xf9: /* graphic control */
- if (Gargs.verbose > 1) {
- fprintf(Glog, " processing graphic control extension @ %#x\n", Ggif.file_pos);
- }
- size = get_byte();
- if (size != 4) {
- longjmp(Ggif.abortion, FAIL_GRAPHIC_EXTENSION_SIZE);
- }
- for (i= 0; (i < size); i++) {
- buffer[i] = get_byte();
- }
- Ggif.control_p = TRUE;
- Ggif.control.dispose = LSR(buffer[0] & 0x1c, 2);
- Ggif.control.user_input_p = ((buffer[0] & 0x02) != NONE);
- Ggif.control.transparent_p = ((buffer[0] & 0x01) != NONE);
- Ggif.control.delay_time = buffer[1] + (256 * buffer[2]);
- if (Ggif.control.transparent_p) {
- Ggif.control.transparent = buffer[3];
- }
- if (get_byte() != 0) {
- longjmp(Ggif.abortion, FAIL_GRAPHIC_EXTENSION_TERMINATOR);
- }
- break;
-
- case 0xfe: /* comment, in 7-bit ascii */
- if (Gargs.verbose > 1) {
- fprintf(Glog, " skipping comment extension @ %#x\n", Ggif.file_pos);
- }
- gif_skip_data();
- break;
-
- case 0x01: /* plain text [a built in text `image'] */
- if (Gargs.verbose > 1) {
- fprintf(Glog, " skipping text extension @ %#x\n", Ggif.file_pos);
- }
- gif_skip_data();
- break;
-
- case 0xff: /* application */
- if (Gargs.verbose > 1) {
- fprintf(Glog, " skipping application extension @ %#x\n", Ggif.file_pos);
- }
- gif_skip_data();
- break;
-
- default:
- if (Gargs.verbose > 1) {
- fprintf(Glog, " skipping unknown extension %d @ %#x\n", extension, Ggif.file_pos);
- }
- gif_skip_data();
- }
- }
-
-
-
- /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- */
-
- static void gif_get_image_desc(void) {
- bits flags;
- int i, size;
- os_colour colour;
-
- if (Gargs.verbose > 1) {
- fprintf(Glog, " reading image description @ %#x\n", Ggif.file_pos);
- }
- Ggif.image.topleft.x = get_word();
- Ggif.image.topleft.y = get_word();
- Ggif.image.size.x = get_word();
- Ggif.image.size.y = get_word();
- if ((Ggif.image.size.x < 1) || (Ggif.image.size.y < 1)) {
- longjmp(Ggif.abortion, FAIL_IMAGE_DIMENSIONS);
- }
- flags = get_byte();
- Ggif.image.palette_bpp = (flags & 0x07) + 1;
- Ggif.image.palette_sorted_p = ((flags & 0x20) != NONE);
- Ggif.image.interlace_p = ((flags & 0x40) != NONE);
- Ggif.image.palette_p = ((flags & 0x80) != NONE);
- if (Gargs.verbose > 0) {
- fprintf(Glog, " image @ (%d, %d), size (%d, %d), bpp= %d, interlaced= %s, palette= %s, sorted= %s\n",
- Ggif.image.topleft.x, Ggif.image.topleft.y, Ggif.image.size.x, Ggif.image.size.y,
- Ggif.image.palette_bpp, YESNO(Ggif.image.interlace_p), YESNO(Ggif.image.palette_p), YESNO(Ggif.image.palette_sorted_p));
- }
- i = 0;
- if (Ggif.image.palette_p) {
- /*
- * global colour map
- */
- size = 1 << Ggif.image.palette_bpp;
- for (; (i < size); i++) {
- colour = get_byte() << 8;
- colour |= get_byte() << 16;
- colour |= get_byte() << 24;
- Ggif.image.palette[i] = colour;
- }
- }
- for (size = 1 << Ggif.screen.palette_bpp; (i < size); i++) {
- Ggif.image.palette[i] = Ggif.screen.palette[i];
- }
- /*
- * we now force the `transparent' entry to black so that does not dither!
- */
- if ((Ggif.control_p) && (Ggif.control.transparent_p)) {
- Ggif.image.palette[Ggif.control.transparent] = os_COLOUR_BLACK;
- }
- }
-
-
-
- /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- * here the GIF file is expecting the initial minimum code size byte
- * what we do is expand the stream into a 256 colour image
- * where each row is Gsprite.pixel_size.x bytes across, starting at Gsprite.image.bitmap
- * and each row is Gsprite.image.line_length bytes apart
- */
-
- static void gif_lzw_decode(void) {
- register byte *sp;
- register byte *bufptr;
- const byte *bufend;
-
- int code, fc, oc;
- int c, size, guard;
-
- /* Initialize for decoding a new image...
- */
- size = get_byte();
- if ((size < 2) || (size > 9)) {
- longjmp(Ggif.abortion, FAIL_CORRUPT_LZW);
- }
- lzw_init(size);
-
- /* Initialize in case they forgot to put in a clear code.
- * (This shouldn't happen, but we'll try and decode it anyway...)
- */
- oc = fc = 0;
-
- /* Set up the Glzw.stack pointer and decode buffer pointer
- */
- sp = Glzw.stack;
- bufptr = gif_row(); bufend = bufptr + Gsprite.pixel_size.x;
-
- /* This is the main loop. For each code we get we pass through the
- * linked list of Glzw.prefix codes, pushing the corresponding "character" for
- * each code onto the Glzw.stack. When the list reaches a single "character"
- * we push that on the Glzw.stack too, and then start unGlzw.stacking each
- * character for output in the correct order. Special handling is
- * included for the Glzw.clear code, and the whole thing ends when we get
- * an Glzw.ending code.
- */
- while (c = lzw_get_next_code(), (c != Glzw.ending)) {
- #if DEBUG_LZW
- fprintf(stderr, "LZW(%#x)\n", c);
- #endif /* DEBUG_LZW */
- if (c == Glzw.clear) {
- #if DEBUG_LZW
- fprintf(stderr, "LZW_CLEAR(%#x)\n", c);
- #endif /* DEBUG_LZW */
- /*
- * If the code is a Glzw.clear code, reinitialize all necessary items.
- */
- Glzw.current_size = size + 1;
- Glzw.slot = Glzw.newcodes;
- Glzw.top_slot = 1 << Glzw.current_size;
-
- #ifndef NDEBUG
- /*
- * help spot bugs/errors quicker
- */
- memset(Glzw.prefix, 0xee, sizeof(Glzw.prefix));
- #endif /* NDEBUG */
-
- /* Continue reading codes until we get a non-Glzw.clear code
- * (Another unlikely, but possible case...)
- */
- while ((c = lzw_get_next_code()) == Glzw.clear) {
- /* do nothing */
- }
- #if DEBUG_LZW
- fprintf(stderr, "LZW_POST_CLEAR(%#x)\n", c);
- #endif /* DEBUG_LZW */
-
- /* If we get an Glzw.ending code immediately after a Glzw.clear code
- * (Yet another unlikely case), then break out of the loop.
- */
- if (c == Glzw.ending) {
- break;
- }
-
- /* Finally, if the code is beyond the range of already set codes,
- * (This one had better NOT happen... I have no idea what will
- * result from this, but I doubt it will look good...) then set it
- * to color zero.
- */
- if (c >= Glzw.slot) {
- c = 0;
- }
-
- if (c >= 0x100) {
- /*
- * a corrupt file
- */
- if (!Gargs.quiet_p) {
- fprintf(stderr, "gif2rpc: %s: lzw-code %d too large\n", Gargs.gif, c);
- }
- if (Gargs.fussy_p) {
- longjmp(Ggif.abortion, FAIL_CORRUPT_LZW);
- }
- break;
- }
-
- assert(c >= 0);
- assert(c < 0x100);
-
- oc = fc = c;
-
- /* And let us not forget to put the char into the buffer... And
- * if, on the off chance, we were exactly one pixel from the end
- * of the line, we have to send the buffer to the out_line()
- * routine...
- */
- *bufptr++ = c;
- if (bufptr == bufend) {
- bufptr = gif_row(); bufend = bufptr + Gsprite.pixel_size.x;
- }
- } else {
- /* In this case, it's not a clear code or an ending code, so
- * it must be a code code... So we can now decode the code into
- * a stack of character codes. (clear as mud, right?)
- */
- code = c;
-
- /* Here we go again with one of those off chances... If, on the
- * off chance, the code we got is beyond the range of those already
- * set up (Another thing which had better NOT happen...) we trick
- * the decoder into thinking it actually got the last code read.
- * (Hmmn... I'm not sure why this works... But it does...)
- */
- if (code >= Glzw.slot) {
- if (code > Glzw.slot) {
- Glzw.bad_code_count++;
- }
- code = oc;
- *sp++ = fc;
- #if DEBUG_LZW
- fprintf(stderr, "LZW_byteOC(%#x)\n", sp[-1]);
- #endif /* DEBUG_LZW */
- }
-
- /* Here we scan back along the linked list of prefixes, pushing
- * helpless characters (ie. suffixes) onto the stack as we do so.
- */
- for (guard= LZW_MAX_CODES; ((code >= Glzw.newcodes) && (guard > 0)); guard--) {
- assert(code >= 0);
- assert(code < COUNT(Glzw.suffix));
- *sp++ = Glzw.suffix[code];
- #if DEBUG_LZW
- fprintf(stderr, "LZW_byte(%#x)\n", sp[-1]);
- #endif /* DEBUG_LZW */
- code = Glzw.prefix[code];
- }
-
- if (code >= 0x100) {
- /*
- * a corrupt file
- */
- if (!Gargs.quiet_p) {
- fprintf(stderr, "gif2rpc: %s: lzw-code %d too large\n", Gargs.gif, code);
- }
- if (Gargs.fussy_p) {
- longjmp(Ggif.abortion, FAIL_CORRUPT_LZW);
- }
- break;
- }
-
- assert(code >= 0);
- assert(code < 0x100);
-
- /* Push the last character on the Glzw.stack, and set up the new
- * Glzw.prefix and Glzw.suffix, and if the required Glzw.slot number is greater
- * than that allowed by the current bit size, increase the bit
- * size. (NOTE - If we are all full, we *don't* save the new
- * Glzw.suffix and Glzw.prefix... I'm not certain if this is correct...
- * it might be more proper to overwrite the last code...
- */
- *sp++ = code;
- #if DEBUG_LZW
- fprintf(stderr, "LZW_byteCODE(%#x)\n", sp[-1]);
- #endif /* DEBUG_LZW */
- if (Glzw.slot < Glzw.top_slot) {
- assert(Glzw.slot >= 0);
- assert(Glzw.slot < 0x100);
- Glzw.suffix[Glzw.slot] = fc = code;
- Glzw.prefix[Glzw.slot++] = oc;
- oc = c;
- }
- if (Glzw.slot >= Glzw.top_slot) {
- if (Glzw.current_size < 12) {
- Glzw.top_slot <<= 1;
- Glzw.current_size++;
- }
- }
-
- /* Now that we've pushed the decoded string (in reverse order)
- * onto the Glzw.stack, lets pop it off and put it into our decode
- * buffer... And when the decode buffer is full, write another
- * line...
- */
- while (sp > Glzw.stack) {
- *bufptr++ = *(--sp);
- if (bufptr == bufend) {
- bufptr = gif_row(); bufend = bufptr + Gsprite.pixel_size.x;
- }
- }
- }
- }
- if ((bufptr != bufend) && ((bufend-bufptr) != Gsprite.pixel_size.x)) {
- /*
- * this probably indicates an error
- * but do NOT fill it because some files seem to have an ``extra'' row!
- */
- if (!Gargs.quiet_p) {
- fprintf(stderr, "gif2rpc: %s: missing %d pixels off last row\n", Gargs.gif, bufend - bufptr);
- }
- if (Gargs.fussy_p) {
- longjmp(Ggif.abortion, FAIL_INCOMPLETE_LAST_ROW);
- }
- }
- if (Glzw.block_count != 0) {
- /*
- * once again, this PROBABLY indicates an error
- * but have seen sprites where an extra `0' byte exists
- */
- if (!Gargs.quiet_p) {
- fprintf(stderr, "gif2rpc: %s: extra %d byte(s) at end of image\n", Gargs.gif, Glzw.block_count);
- }
- if (Gargs.fussy_p) {
- longjmp(Ggif.abortion, FAIL_EXTRA_DATA_AFTER_IMAGE);
- }
- } else {
- for (guard= Glzw.block_count; (guard); guard--) {
- code = get_byte();
- }
- }
- if (get_byte() != 0) {
- if (Gargs.fussy_p) {
- if (!Gargs.quiet_p) {
- fprintf(stderr, "gif2rpc: %s: unexpected byte at end of image\n", Gargs.gif);
- }
- longjmp(Ggif.abortion, FAIL_BAD_EOF_IMAGE);
- }
- }
- }
-
-
-
- /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- */
-
- static void lzw_init(
- int size) {
- Glzw.current_size = size + 1;
- Glzw.top_slot = 1 << Glzw.current_size;
- Glzw.clear = 1 << size;
- Glzw.ending = Glzw.clear + 1;
- Glzw.slot = Glzw.newcodes = Glzw.ending + 1;
- Glzw.nbits_left = 0;
-
- Glzw.row = 0;
- Glzw.interlace_part = 0;
- Glzw.block_count = 0;
-
- Glzw.real_row = 0;
- }
-
-
-
- /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- */
-
- static int lzw_get_next_code(void) {
- unsigned int ret;
-
- if (Glzw.nbits_left == 0) {
- Glzw.b1 = get_image_byte();
- Glzw.nbits_left = 8;
- }
-
- ret = LSR(Glzw.b1, (8 - Glzw.nbits_left));
- while (Glzw.current_size > Glzw.nbits_left) {
- Glzw.b1 = get_image_byte();
- ret |= Glzw.b1 << Glzw.nbits_left;
- Glzw.nbits_left += 8;
- }
- Glzw.nbits_left -= Glzw.current_size;
- ret &= ~((~0) << Glzw.current_size);
- return ret;
- }
-
-
-
- /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- */
-
- static byte *gif_row(void) {
- int row;
- static const int Ggif_interlace_offset[4] = {0, 4, 2, 1}; /* The way Interlaced image should. */
- static const int Ggif_interlace_jumps[4] = {8, 8, 4, 2}; /* be read - offsets and jumps... */
-
- #if DEBUG_LZW
- fprintf(stderr, "gif_row()\n");
- #endif /* DEBUG_LZW */
- if (Gargs.hourglass_p) {
- row = Glzw.real_row++;
- xhourglass_percentage((row * 100) / Gsprite.pixel_size.y);
- }
- row = Glzw.row++;
- if (Ggif.image.interlace_p) {
- assert(Glzw.interlace_part < 4);
- row *= Ggif_interlace_jumps[Glzw.interlace_part];
- row += Ggif_interlace_offset[Glzw.interlace_part];
- if (row >= Ggif.image.size.y) {
- Glzw.interlace_part++;
- if (Glzw.interlace_part < 4) {
- row = Ggif_interlace_offset[Glzw.interlace_part];
- Glzw.row = 1;
- }
- }
- }
- if (Gargs.verbose > 1) {
- fprintf(Glog, " processing row %d @ %#x in file\n", row, Ggif.file_pos);
- }
- row = MIN(row, Ggif.image.size.y - 1);
- return &Gsprite.image.bitmap[row * Gsprite.image.line_length];
- }
-
-
-
- /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- */
-
- static void gif_skip_data(void) {
- int size;
- byte dummy;
-
- for (; (size = get_byte(), (size > 0));) {
- do {
- dummy = get_byte();
- size--;
- } while (size != 0);
- }
- }
-
-
-
- /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- */
-
- static byte get_image_byte(void) {
- if (Glzw.block_count == 0) {
- return get_image_byte_aux();
- }
- Glzw.block_count--;
- return get_byte();
- }
-
-
-
- /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- */
-
- static byte get_image_byte_aux(void) {
- Glzw.block_count = get_byte();
- return get_image_byte();
- }
-
-
-
- /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- */
-
- static byte get_byte(void) {
- if (Ggif.file_pos >= Ggif.file_siz) {
- return fail();
- }
- return Ggif.file_buffer[Ggif.file_pos++];
- }
-
-
-
- /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- */
-
- static int get_word(void) {
- unsigned int i;
-
- i = get_byte();
- return i | (get_byte() << 8);
- }
-
-
-
- /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- */
-
- static byte fail(void) {
- longjmp(Ggif.abortion, FAIL_SHORT_FILE);
- return '\0';
- }
-
-
-
- /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- * this should be called AFTER know gif image size and palette
- * it sets up the Gsprite variable
- */
-
- static void prepare_sprite(void) {
- int aspect_ratio;
- bits flags;
- int lb_bpp;
- os_mode mode;
- size_t area_size;
- char *name;
- int i;
- osspriteop_header *sprite;
- char name_buffer[osspriteop_NAME_LIMIT + 1];
- os_error *error;
- int palette_size;
-
- Gsprite.pixel_size = Ggif.image.size; /* structure copy */
- assert(Gsprite.pixel_size.x > 0);
- assert(Gsprite.pixel_size.y > 0);
- aspect_ratio = 15 + ((Ggif.screen.aspect_ratio) ? Ggif.screen.aspect_ratio : 49);
- if (aspect_ratio != 64) {
- if (Gargs.verbose > 0) {
- fprintf(Glog, " aspect ratio is %d/64\n", aspect_ratio);
- }
- if (Gargs.square_p) {
- aspect_ratio = 64;
- }
- }
- Gsprite.dpi.x = (90 * aspect_ratio) / 64;
- Gsprite.dpi.y = 90;
- Gsprite.transparent_p = ((Ggif.control_p) ? (Ggif.control.transparent_p) : FALSE);
-
- palette_size = 0;
- switch (Gargs.bpp) {
- case -1:
- flags = os_read_mode_variable(os_CURRENT_MODE, os_MODEVAR_LOG2_BPP, &lb_bpp);
- if ((flags & _C) != NONE) {
- if (!Gargs.quiet_p) {
- fprintf(stderr, "gif2rpc: failed to find screen mode\n");
- }
- exit(EXIT_FAILURE);
- }
- if (lb_bpp <= 3) {
- error = xcolourtrans_read_palette(
- (osspriteop_area const *)colourtrans_CURRENT_MODE,
- (osspriteop_id)colourtrans_CURRENT_PALETTE,
- (os_palette *)Goutput_palette,
- sizeof(Goutput_palette),
- NONE,
- NULL);
- if (error) {
- if (!Gargs.quiet_p) {
- fprintf(stderr, "gif2rpc: unable to read palette (%#x): '%s'\n", error->errnum, error->errmess);
- }
- exit(EXIT_FAILURE);
- }
- palette_size = sizeof(os_colour_pair) * (1 << (1 << lb_bpp));
- }
- break;
- case 0:
- lb_bpp = 3;
- palette_size = sizeof(os_colour_pair) * 256; /* always 256 entries now */
- if (Ggif.input_bpp < 1) {
- assert(!"internal error");
- } else if (Ggif.input_bpp < 2) {
- lb_bpp = 0;
- } else if (Ggif.input_bpp < 3) {
- lb_bpp = 1;
- } else if (Ggif.input_bpp < 5) {
- lb_bpp = 2;
- }
- break;
- case 1: lb_bpp = 0; break;
- case 2: lb_bpp = 1; break;
- case 4: lb_bpp = 2; break;
- case 8: lb_bpp = 3; break;
- case 16: lb_bpp = 4; break;
- case 32: lb_bpp = 5; break;
- default:
- if (!Gargs.quiet_p) {
- fprintf(stderr, "gif2rpc: invalid command line: bpp value %d, should be one of {0,1,2,4,8,16,32}\n", Gargs.bpp);
- }
- exit(EXIT_FAILURE);
- }
- if ((lb_bpp <= 3) && (palette_size == 0)) {
- default_wimppalette(Goutput_palette, lb_bpp);
- }
- Ggif.output_lb_bpp = lb_bpp;
-
- /*
- * we will generate a 256 colour sprite, and then downgrade it if necessary
- */
- lb_bpp = MAX(3, lb_bpp);
-
- Gsprite.lb_bpp = lb_bpp;
-
- if (Gargs.name == NULL) {
- if (Gargs.autoname_p) {
- int len;
-
- name = strrchr(Gargs.gif, '.');
- if (!name) {
- name = strrchr(Gargs.gif, ':');
- }
- if (!name) {
- name = Gargs.gif;
- } else {
- name++;
- }
- len = strlen(name);
- if (len > osspriteop_NAME_LIMIT) {
- name += len - osspriteop_NAME_LIMIT;
- }
- name = strcpy(name_buffer, name);
- } else {
- if (sizeof(SPRITE_NAME) >= sizeof(name_buffer)) {
- abort();
- }
- name = strcpy(name_buffer, SPRITE_NAME);
- }
- } else {
- name = Gargs.name;
- }
- for (i= 0; (i < osspriteop_NAME_LIMIT); i++) {
- if ((name[i] <= 32) || (name[i] == 0x7f)) {
- break;
- }
- name[i] = (char)tolower(name[i]);
- }
- name[i] = '\0';
-
- mode = (lb_bpp == 3) ? os_MODE8BPP90X90 : MODE(lb_bpp, Gsprite.dpi.x, Gsprite.dpi.y);
-
- Gsprite.image.line_length = 4 * WORD_WIDTH(Gsprite.pixel_size.x * (1 << lb_bpp));
- Gsprite.mask.line_length = 4 * WORD_WIDTH(Gsprite.pixel_size.x * ((lb_bpp <= 3) ? (1 << lb_bpp) : 1));
- /*
- * get a sprite area big enough
- */
- area_size = sizeof(osspriteop_area)
- + sizeof (osspriteop_header)
- + palette_size
- + (Gsprite.image.line_length * Gsprite.pixel_size.y)
- + ((Gsprite.transparent_p)
- ? (Gsprite.mask.line_length * Gsprite.pixel_size.y)
- : 0);
-
- Gsprite.area = malloc(area_size);
- if (!Gsprite.area) {
- if (!Gargs.quiet_p) {
- fprintf(stderr, "gif2rpc: out of memory (need %u)\n", area_size);
- }
- exit(EXIT_FAILURE);
- }
-
- Gsprite.area->size = area_size;
- Gsprite.area->first = sizeof(osspriteop_area);
- osspriteop_clear_sprites(osspriteop_USER_AREA, Gsprite.area);
- osspriteop_create_sprite(osspriteop_NAME, Gsprite.area, name, FALSE, Gsprite.pixel_size.x, Gsprite.pixel_size.y, mode);
- sprite = osspriteop_select_sprite(osspriteop_NAME, Gsprite.area, (osspriteop_id)name);
- assert(sprite);
- Gsprite.sprite = sprite;
- if (Gsprite.transparent_p) {
- osspriteop_create_mask(osspriteop_PTR, Gsprite.area, (osspriteop_id)sprite);
- }
- if (palette_size != 0) {
- int i;
- os_colour_pair *palette = (os_colour_pair *)(sprite+1);
- const os_colour *in;
-
- insert_bytes_before_image(Gsprite.area, sprite, palette_size);
- /*
- * manually insert palette
- */
- in = (Gargs.bpp == 0) ? Ggif.image.palette : Goutput_palette;
- for (i= 0; (i < (palette_size / sizeof(os_colour_pair))); i++) {
- palette[i].on = palette[i].off = in[i];
- }
- }
- Gsprite.image.bitmap = ((byte *)sprite) + sprite->image;
- Gsprite.mask.bitmap = ((byte *)sprite) + sprite->mask;
- }
-
-
-
- /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- */
-
- static void insert_bytes_before_image(
- osspriteop_area *area,
- osspriteop_header *sprite,
- int siz) {
- assert(area);
- assert(sprite);
- assert(siz >= 0);
-
- area->used += siz;
- assert(area->used <= area->size);
-
- sprite->size += siz;
- sprite->image += siz;
- sprite->mask += siz;
- }
-
-
-
- /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- */
-
- static void load_file(void) {
- int type;
- bits file_type;
- int siz;
-
- if (Gargs.verbose > 0) {
- fprintf(Glog, "processing GIF file `%s'\n", Gargs.gif);
- }
- type = osfile_read_stamped(Gargs.gif, NULL, NULL, (int *)&Ggif.file_siz, NULL, &file_type);
- if (type != osfile_IS_FILE) {
- if (!Gargs.quiet_p) {
- fprintf(stderr, "gif2rpc: %s: this is not a (GIF) file\n", Gargs.gif);
- }
- exit(EXIT_FAILURE);
- }
- if (Gargs.fussy_p) {
- if (file_type != 0xff0) {
- if (!Gargs.quiet_p) {
- fprintf(stderr, "gif2rpc: %s: this is not typed as a GIF file\n", Gargs.gif);
- }
- exit(EXIT_FAILURE);
- }
- }
- if (Gargs.verbose > 1) {
- fprintf(Glog, " GIF file is %#x bytes long\n", Ggif.file_siz);
- }
- Ggif.file_buffer = malloc(MAX(1, Ggif.file_siz));
- if (!Ggif.file_buffer) {
- if (!Gargs.quiet_p) {
- fprintf(stderr, "gif2rpc: out of memory (need %u)\n", Ggif.file_siz);
- }
- exit(EXIT_FAILURE);
- }
- osfile_load_stamped(Gargs.gif, (byte *)Ggif.file_buffer, NULL, NULL, &siz, NULL);
- if (siz != Ggif.file_siz) {
- if (!Gargs.quiet_p) {
- fprintf(stderr, "gif2rpc: %s: cant read file\n", Gargs.gif);
- }
- exit(EXIT_FAILURE);
- }
- }
-
-
-
- /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- */
-
- static void turn_off_hourglass(void) {
- xhourglass_off();
- }
-
-
-
- /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- */
-
- static void process_mask(void) {
- const byte *source;
- byte *dest;
- int x, y;
- byte transparent;
- int width;
- unsigned int b;
-
- source = Gsprite.image.bitmap;
- dest = Gsprite.mask.bitmap;
- assert(dest);
- transparent = Ggif.control.transparent;
- width = Gsprite.pixel_size.x;
-
- switch (Gsprite.lb_bpp) {
- case 3:
- /*
- * create an 8bpp mask
- */
- if (Gargs.verbose > 0) {
- fprintf(Glog, " created 8bpp transparency mask\n");
- }
- for (y= Gsprite.pixel_size.y; (y > 0); y--) {
- for (x= width-1; (x >= 0); x--) {
- dest[x] = (source[x] == transparent) ? 0x00 : 0xff;
- }
- source += Gsprite.image.line_length;
- dest += Gsprite.mask.line_length;
- }
- break;
- case 4:
- case 5:
- /*
- * create a 1bpp mask
- */
- if (Gargs.verbose > 0) {
- fprintf(Glog, " created 1bpp transparency mask\n");
- }
- for (y= Gsprite.pixel_size.y; (y > 0); y--) {
- b = ~0u;
- for (x= 0; (x < width);) {
- if (source[x] == transparent) {
- b &= ~(1u << (x & 0x1f));
- }
- x++;
- if ((x & 0x1f) == 0) {
- *(((unsigned int *)dest) + LSR(x, 5) - 1) = b;
- b = ~0u;
- }
- }
- if (x & 0x1f) {
- *(((unsigned int *)dest) + LSR(x, 5)) = b;
- }
- source += Gsprite.image.line_length;
- dest += Gsprite.mask.line_length;
- }
- break;
- default:
- assert(!"internal error");
- longjmp(Ggif.abortion, FAIL_INTERNAL_PROCESS_MASK);
- }
- }
-
-
-
- /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- */
-
- static void process_image(void) {
- int i;
- process_gif_fn fn;
-
- Gpg.buffer = Gsprite.image.bitmap;
- Gpg.pixel_width = Gsprite.pixel_size.x;
- Gpg.pixel_height = Gsprite.pixel_size.y;
- Gpg.line_length = Gsprite.image.line_length;
- Gpg.in_palette.colours = Ggif.image.palette;
- Gpg.in_palette.ncolours = 1 << Ggif.input_bpp;
- switch (Gsprite.lb_bpp) {
- case 3:
- /*
- * this is a special case
- */
- if (Gargs.bpp == 0) {
- /*
- * we want to preserve as much info as possible of the GIF
- * but if can reduce bpp then might as well:
- */
- if (Ggif.output_lb_bpp < 3) {
- reduce_sprite_lb_bpp(Ggif.output_lb_bpp);
- }
- return;
- }
- Gpg.fn = map_scaled_rgb_to_palette_index;
- if ((!Gargs.accurate_p) && (Ggif.output_lb_bpp == 3) && (Ggif.input_bpp != 0)) {
- Gpg.fn = map_scaled_rgb_to_8bpp_colour_number_quick;
- }
-
- Gpg.out_palette.ncolours = 1 << (1 << Ggif.output_lb_bpp);
- Gpg.out_palette.colours = malloc(sizeof(*Gpg.out_palette.colours) * Gpg.out_palette.ncolours);
- if (!Gpg.out_palette.colours) {
- if (!Gargs.quiet_p) {
- fprintf(stderr, "gif2rpc: %s: out of memory\n", Gargs.gif);
- }
- return;
- }
- {
- int index;
- const os_colour *palette;
- rgbtuple *tuples;
- os_colour colour;
- int red, grn, blu;
-
- palette = Goutput_palette;
- tuples = Gpg.out_palette.colours;
- for (index= Gpg.out_palette.ncolours - 1; (index >= 0); index--) {
- colour = palette[index];
- red = (colour >> (1 * 8)) & 0xff; red |= red << 8;
- grn = (colour >> (2 * 8)) & 0xff; grn |= grn << 8;
- blu = (colour >> (3 * 8)) & 0xff; blu |= blu << 8;
- tuples[index].blu = blu;
- tuples[index].grn = grn;
- tuples[index].red = red;
- }
- }
- break;
- case 4:
- Gpg.fn = (Gargs.accurate_p)
- ? map_scaled_rgb_to_16bpp_colour_accurate
- : map_scaled_rgb_to_16bpp_colour_quick;
- break;
- case 5:
- Gpg.fn = NULL;
- break;
- default:
- assert(!"internal error");
- }
-
- if (!Gargs.filter) {
- i = 1; /* 2x2 dither */
- } else {
- for (i= 0; (i < COUNT(Gfilters)); i++) {
- if (stricmp(Gfilters[i].name, Gargs.filter) == 0) {
- break;
- }
- }
- if (i == COUNT(Gfilters)) {
- if (!Gargs.quiet_p) {
- fprintf(stderr, "gif2rpc: unrecognised filter `%s' specified on command line\n",
- Gargs.filter);
- }
- if (Gargs.fussy_p) {
- exit(EXIT_FAILURE);
- }
- i = 1; /* 2x2 dither */
- }
- }
- assert(i >= 0);
- assert(i < COUNT(Gfilters));
-
- Gsprite.filter = i;
-
- if (Gargs.verbose > 0) {
- fprintf(Glog,
- " %s conversion to %dbpp\n",
- Gfilters[Gsprite.filter].name, 1 << Gsprite.lb_bpp);
- }
-
- fn = filter_fn(Gsprite.filter);
- if (!fn) {
- if (!Gargs.quiet_p) {
- fprintf(stderr,
- "gif2rpc: %s: %s conversion to %dbpp not implemented, using 2x2 dither\n",
- Gargs.gif, Gfilters[i].name, 1 << Gsprite.lb_bpp);
- }
- /*
- * default to 2x2 dither if not implemented others yet
- */
- Gsprite.filter = 1;
- fn = filter_fn(Gsprite.filter);
- assert(fn);
- }
- if ((*fn)(&Gpg)) {
- /*
- * oh dear, out of memory
- */
- if (!Gargs.quiet_p) {
- fprintf(stderr,
- "gif2rpc: %s: out of memory using %s filter\n",
- Gargs.gif, Gfilters[Gsprite.filter].name);
- }
- if (Gargs.fussy_p) {
- exit(EXIT_FAILURE);
- }
- if (Gsprite.filter > 1) {
- /*
- * default to 2x2 dither (uses no memory)
- */
- Gsprite.filter = 1;
- fn = filter_fn(Gsprite.filter);
- assert(fn);
- (*fn)(&Gpg);
- }
- }
- if ((Gsprite.lb_bpp == 3)
- && (Ggif.output_lb_bpp < 3)) {
- reduce_sprite_lb_bpp(Ggif.output_lb_bpp);
- }
- }
-
-
-
- /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- */
-
-
- static process_gif_fn filter_fn(
- int index) {
- process_gif_fn fn = NULL;
-
- assert(index >= 0);
- assert(index < COUNT(Gfilters));
-
- switch (Gsprite.lb_bpp) {
- case 3:
- fn = Gfilters[index].fn_8bpp;
- break;
-
- case 4:
- fn = (Gargs.force_66_p)
- ? Gfilters[index].fn_16bpp_66bit
- : Gfilters[index].fn_16bpp_48bit;
- break;
-
- case 5:
- fn = process_gif_32bpp;
- break;
-
- default:
- assert(!"internal error");
- longjmp(Ggif.abortion, FAIL_INTERNAL_PROCESS_IMAGE);
- }
- return fn;
- }
-
- /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- * this unconditionally converts an 8bpp image into either a 8bpp, 4bpp, 2bpp, or 1bpp image
- * note that converting to 8bpp is useful if the source is a 16bpp image...
- * note that the original *must* have a 256 entry palette attached
- */
-
- static void reduce_sprite_lb_bpp(
- int lb_bpp) {
- int line_length;
- int height;
- byte *rove;
- int original_size;
- int ppw;
- int bpp;
- reducebitmap rb;
- osspriteop_header *sprite = Gsprite.sprite;
- static const os_mode modes[4] = {
- os_MODE1BPP90X90, os_MODE2BPP90X90,
- os_MODE4BPP90X90, os_MODE8BPP90X90};
-
- assert(sprite);
- assert(lb_bpp >= 0);
- assert(lb_bpp <= 3);
-
- Gsprite.lb_bpp = lb_bpp;
- sprite->mode = modes[lb_bpp];
- bpp = 1 << lb_bpp;
- if (Gargs.verbose > 0) {
- fprintf(Glog, " reducing image to %d bpp\n", bpp);
- }
- /*
- * the LEGAL way to do this is to create a sprite of the required size,
- * switch output to it, and then render our 256 colour sprite into it
- * but fuck that, let's just move that memory about ourselves, it is
- * easier
- */
- line_length = 4 * WORD_WIDTH(Gsprite.pixel_size.x * bpp);
- height = Gsprite.pixel_size.y;
- rove = (byte *)(sprite + 1); /* start of palette */
- if (sprite->image == sizeof(osspriteop_header)) {
- /*
- * no palette, so must be using default palette!
- */
- if (Gargs.verbose > 0) {
- fprintf(Glog, " the reduced image uses the default palette\n");
- }
- } else {
- /*
- * check original palette was wide enough!
- */
- assert(sprite->image >= (((1 << bpp) * sizeof(os_colour_pair)) + sizeof(osspriteop_header)));
- rove += (1 << bpp) * sizeof(os_colour_pair); /* end of palette */
- if (Gargs.verbose > 0) {
- fprintf(Glog, " the reduced image has a palette\n");
- }
- }
- sprite->width = WORD_WIDTH(Gsprite.pixel_size.x * bpp) - 1; /* must do this! */
- ppw = 32 / bpp;
- sprite->right_bit = 31 - (bpp * (Gsprite.pixel_size.x % ppw)); /* and must do this! */
- sprite->image = rove - ((byte *)sprite); /* offset of (new) image */
- rb.source.bitmap = Gsprite.image.bitmap;
- rb.source.line_length = Gsprite.image.line_length;
- rb.dest.bitmap = rove;
- rb.dest.line_length = line_length;
- rb.bpp = bpp;
- rb.pixel_size = Gsprite.pixel_size; /* structure copy */
- reduce_bitmap(&rb); /* process image */
- Gsprite.image.bitmap = rove; /* set up incase re-process! */
- rove += line_length * height; /* end of image */
- if (Gsprite.transparent_p) {
- if (Gargs.verbose > 0) {
- fprintf(Glog, " reducing transparency to %d bpp\n", bpp);
- }
- assert(sprite->image <= sprite->mask); /* we make this assumption */
- assert(Gsprite.mask.line_length == Gsprite.image.line_length); /* I should hope so! */
- assert(Gsprite.mask.bitmap);
- sprite->mask = rove - ((byte *)sprite); /* offset of transparency */
- rb.source.bitmap = Gsprite.mask.bitmap;
- rb.dest.bitmap = rove;
- reduce_bitmap(&rb); /* process transparency */
- Gsprite.mask.bitmap = rove; /* set up incase re-process! */
- Gsprite.mask.line_length = line_length; /* set up incase re-process! */
- rove += line_length * height; /* end of transparency */
- } else {
- sprite->mask = sprite->image;
- }
- Gsprite.image.line_length = line_length; /* set up incase re-process! */
- original_size = sprite->size;
- sprite->size = rove - ((byte *)sprite);
- Gsprite.area->used -= original_size - sprite->size;
- }
-
-
-
- /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- * this takes a source image of resolution 8bpp
- * and converts it to a destination image of resolution rb->bpp (8/4/2/1)
- * it will use the lower rb->bpp of each source byte
- * note can overlap, so long as if dest <= source
- */
-
- static void reduce_bitmap(
- const reducebitmap *rb) {
- int x, y;
- int pixel_width, word_width, bit_width;
- int bpp;
- int bit;
- bits w, mask;
- byte *source;
- bits *dest;
- int ppw;
- int ds, dd;
-
-
- assert(rb);
- assert(rb->source.bitmap);
- assert(rb->dest.bitmap);
- assert(rb->dest.line_length > 0);
- assert((rb->dest.line_length % 4) == 0); /* since we are writing words at a time */
- assert((rb->bpp == 8) || (rb->bpp == 4) || (rb->bpp == 2) || (rb->bpp == 1));
-
-
- bpp = rb->bpp;
- bit_width = rb->pixel_size.x * bpp;
- word_width = WORD_WIDTH(bit_width);
- assert((word_width * 4) <= ABS(rb->dest.line_length));
- assert(ABS(rb->source.line_length) >= ABS(rb->dest.line_length));
- ppw = 32 / bpp;
- pixel_width = word_width * ppw;
- assert(pixel_width >= rb->pixel_size.x);
- assert(pixel_width < (rb->pixel_size.x+ppw));
- source = rb->source.bitmap;
- dest = (bits *)rb->dest.bitmap;
- mask = ~(~0u << bpp);
- ds = rb->source.line_length - pixel_width;
- dd = LSR(rb->dest.line_length, 2) - word_width;
- for (y= rb->pixel_size.y; (y > 0); y--) {
- /*
- * go left to right because may have source == dest
- */
- assert(source == (rb->source.bitmap + ((rb->pixel_size.y - y) * rb->source.line_length)));
- assert(dest == (bits *)(rb->dest.bitmap + ((rb->pixel_size.y - y) * rb->dest.line_length)));
- x= pixel_width;
- do {
- w = 0;
- bit = 0;
- do {
- w |= (mask & *source++) << bit; /* mask to handle transparency layer */
- bit += bpp;
- } while (bit < 32);
- *dest++ = w;
- x -= ppw;
- } while (x > 0);
- assert(x == 0);
- source += ds;
- dest += dd;
- }
- }
-
-
-
- /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- */
-
- static bool process_gif_32bpp(
- const process_gif *p) {
- const byte *source;
- bits *dest;
- int x, y, width, line_length;
- const os_colour *palette;
-
- assert(p);
- assert(p->in_palette.colours);
- assert(p->buffer);
-
- palette = p->in_palette.colours;
- source = p->buffer;
- line_length = p->line_length;
- width = p->pixel_width;
- for (y= p->pixel_height; (y > 0); y--) {
- dest = (bits *)source;
- /*
- * note HAVE to work from right to left because source == dest
- */
- for (x= width - 1; (x >= 0); x--) {
- dest[x] = LSR(palette[source[x]], 8);
- }
- source += line_length;
- }
- return FALSE;
- }
-
-
-
- /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- * output filename in a `nice' manner into a 15 character field
- */
-
- static void log_filename(
- const char *name) {
- int n;
-
- assert(name);
- n = strlen(name);
- if (n < 15) {
- fprintf(Glog, "%15s", name);
- } else {
- fprintf(Glog, "\x8c%s", name + n - 14);
- }
- }
-
-
-
- /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- */
-
- static int stricmp(
- const char *sleft,
- const char *sright) {
- const char *left, *right;
- int i;
-
- assert(sleft);
- assert(sright);
- left = sleft;
- right = sright;
- if (left == right) {
- return 0; /* need this check for assert code */
- }
- while (*left) {
- if (*right == '\0') {
- return *left; /* left > right */
- }
- assert(left != sright); /* string overlap! */
- assert(right != sleft); /* string overlap! */
- i = tolower(*left) - tolower(*right);
- if (i != 0) {
- return i; /* left != right */
- }
- right++;
- left++;
- }
- return -*right; /* left <= right */
- }
-
-
-
-
- /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- * fills out a palette with entries for the default palette that the wimp assumes for the given
- * log2 bits per pixel specified
- */
-
- static void default_wimppalette(
- os_colour *palette,
- int lb_bpp) {
- static const os_colour default_wimp_palette_4bpp[16] = {
- os_COLOUR_WHITE, /* 0 */
- os_COLOUR_VERY_LIGHT_GREY, /* 1 */
- os_COLOUR_LIGHT_GREY, /* 2 */
- os_COLOUR_MID_LIGHT_GREY, /* 3 */
- os_COLOUR_MID_DARK_GREY, /* 4 */
- os_COLOUR_DARK_GREY, /* 5 */
- os_COLOUR_VERY_DARK_GREY, /* 6 */
- os_COLOUR_BLACK, /* 7 */
- os_COLOUR_DARK_BLUE, /* 8 */
- os_COLOUR_LIGHT_YELLOW, /* 9 */
- os_COLOUR_LIGHT_GREEN, /* a */
- os_COLOUR_LIGHT_RED, /* b */
- os_COLOUR_CREAM, /* c */
- os_COLOUR_DARK_GREEN, /* d */
- os_COLOUR_ORANGE, /* e */
- os_COLOUR_LIGHT_BLUE}; /* f */
- static const os_colour default_wimp_palette_2bpp[4] = {
- os_COLOUR_WHITE, /* 0 */
- os_COLOUR_LIGHT_GREY, /* 1 */
- os_COLOUR_MID_DARK_GREY, /* 2 */
- os_COLOUR_BLACK}; /* 3 */
- static const os_colour default_wimp_palette_1bpp[2] = {
- os_COLOUR_WHITE, /* 0 */
- os_COLOUR_BLACK}; /* 1 */
- static const os_colour *defaults[3] = {
- default_wimp_palette_1bpp,
- default_wimp_palette_2bpp,
- default_wimp_palette_4bpp};
-
- assert(palette);
-
- switch (lb_bpp) {
- case 0:
- case 1:
- case 2:
- memcpy(palette, defaults[lb_bpp], sizeof(os_colour) << (1 << lb_bpp));
- break;
- case 3:
- {
- int red, grn, blu, tnt, idx;
-
- for (idx= 0; (idx < 256); idx++) {
- /* idx == %BGgRbrTt */
- red = ((idx & 0x10) >> 1) | ((idx & 0x04) >> 0);
- grn = ((idx & 0x40) >> 3) | ((idx & 0x20) >> 3);
- blu = ((idx & 0x80) >> 4) | ((idx & 0x08) >> 1);
- tnt = idx & 0x03;
- red |= tnt; red |= red << 4; /* expand 4 bit -> 8 bit */
- grn |= tnt; grn |= grn << 4;
- blu |= tnt; blu |= blu << 4;
- palette[idx] = (red << os_RSHIFT) | (grn << os_GSHIFT) | (blu << os_BSHIFT);
- }
- }
- break;
- default:
- assert(!"internal error");
- }
- }
-
-
-
- /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- */
-
-
- static void process_cli(
- char **argv) {
-
- char *cli = NULL;
- char *default_params;
- char *arg;
- int i;
-
- assert(argc >= 0);
- assert(argv);
-
- /*
- * note we NEED to slurp in C-parsed arguments because
- * a) os_get_env() will include any '>' redirection bits
- * b) os_get_env() will not handle long command lines
- *
- * put in environment variable args first so cli can overide 'em
- *
- * use our own command line parsing because os_read_args() does not like
- * duplicate options
- */
-
- default_params = getenv(GIF2RPC_PARAMS);
- if (default_params) {
- add_arg(&cli, default_params);
- }
- for (i= 1; (argv[i]); i++) {
- add_arg(&cli, argv[i]);
- }
-
- #define NEXTARG strtok(NULL, " \t")
- #define BOOLEANARG(ARG) } else if (stricmp(arg, "-" # ARG) == 0) { Gargs . ARG ## _p = !Gargs . ARG ## _p;
- #define STRINGARG(ARG) } else if (stricmp(arg, "-" # ARG) == 0) { arg = NEXTARG; if (arg) Gargs . ARG = arg; else { Gargs.help_p = TRUE; break; }
- #define INTEGERARG(ARG) } else if (stricmp(arg, "-" # ARG) == 0) { arg = NEXTARG; if (arg) Gargs . ARG = (int)strtol(arg, NULL, 10); else { Gargs.help_p = TRUE; break; }
- #define DEFAULTARG(ARG) } else if (!Gargs . ARG) { Gargs . ARG = arg;
-
- for (arg= strtok(cli, " \t"); (arg); arg= NEXTARG) {
- if (FALSE) {
- STRINGARG(gif)
- STRINGARG(sprite)
- STRINGARG(filter)
- STRINGARG(name)
- STRINGARG(log)
- BOOLEANARG(help)
- BOOLEANARG(square)
- BOOLEANARG(stats)
- BOOLEANARG(fussy)
- BOOLEANARG(accurate)
- BOOLEANARG(force_66)
- BOOLEANARG(quiet)
- BOOLEANARG(hourglass)
- BOOLEANARG(square)
- BOOLEANARG(autoname)
- INTEGERARG(bpp)
- INTEGERARG(verbose)
- } else {
- if (arg[0] == '-') {
- /*
- * unrecognised option
- */
- Gargs.help_p = TRUE;
- break;
- }
- if (FALSE) {
- DEFAULTARG(gif)
- DEFAULTARG(sprite)
- DEFAULTARG(filter)
- DEFAULTARG(log)
- DEFAULTARG(name)
- } else {
- /*
- * not an implicit argument
- */
- Gargs.help_p = TRUE;
- break;
- }
- }
- }
-
- if ((Gargs.help_p)
- || (Gargs.gif == NULL)
- || (Gargs.sprite == NULL)) {
- fprintf(stderr,
- "gif2rpc: invalid command line `%s'\n"
- "gif2rpc [-gif] <file> [-sprite] <file> [-filter] <name> [-log] <file> "
- "[-bpp <n>] [-verbose <n>] "
- "[-quiet] [-hourglass] [-accurate] [-force_66] "
- "[-name <sprite>]\n"
- "gif gif file to convert\n"
- "sprite name for output sprite file\n"
- "bpp bits-per-pixel for output sprite [-1/0/1/2/4/8/16/32]\n"
- " 0 => output with gif's palette, as 1/2/4/8 bpp sprite\n"
- " -1 => output as current mode with current palette\n"
- " >0 => output with default (no) palette in specified bpp\n"
- "quiet do not print anything if an error occurs --- see log, verbose\n"
- "verbose how much detail do you want 0/1/2? --- see log, quiet\n"
- "log file to which error and status information is sent --- see quiet, verbose\n"
- "hourglass enable hourglass\n"
- "name name of sprite in file <forced to lower case>\n"
- "autoname name of sprite in file derives from the file name\n"
- "square ignore aspect ratio (if output bpp >= 16)\n"
- "accurate use slower code to output 32K/<= 256 colour images\n"
- "force_66 force use of 66-bit accuracy when outputing 32K images\n"
- "filter one of: ",
- cli);
- for (i= 0; (i < COUNT(Gfilters)); i++) {
- fprintf(stderr, "%s%s", Gfilters[i].name, (i == (COUNT(Gfilters)-1)) ? "\n": ", ");
- }
- exit(EXIT_SUCCESS);
- }
- /*
- * note can't free(cli) here because Gargs contains pointers into it for
- * its' string arguments
- */
- }
-